/**
 * 日期处理工具?
 */
package com.wingsoft.common.utils;


import org.springframework.lang.NonNull;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;

/**
 * @author guohui
 */
public class DateUtil {

    public enum DateEnum {
        /**
         * 周 日 时 分 秒的毫秒数
         */
        MS(1L),
        SECOND(1000L),
        MINUTE(SECOND.getMillis() * 60L),
        HOUR(MINUTE.getMillis() * 60L),
        DAY(HOUR.getMillis() * 24L),
        WEEK(DAY.getMillis() * 7L),
        MONTH(DAY.getMillis() * 24L * 30L),
        YEAR(DAY.getMillis() * 24L * 30L * 365L);

        DateEnum(long millis) {
            this.millis = millis;
        }

        // 毫秒
        private final long millis;

        public long getMillis() {
            return millis;
        }
    }


    public static final String FORMATER_1 = "yyyyMMdd";
    public static final String FORMATER_2 = "yyyy-MM-dd";
    public static final String FORMATER_3 = "yyyy-MM-dd HH:mm:ss";
    public static final String FORMATER_4 = "yyyy-MM-dd HH:mm:ss:SSS";
    public static final String FORMATER_5 = "HH";
    public static final String FORMATER_6 = "yyyyMMddHHmm";
    public static final String FORMATER_7 = "yyMMdd";
    public static final String FORMATER_8 = "yyyy-MM-dd HH:mm";
    public static final String FORMATER_9 = "yyyyMMddHHmmss";
    public static final String FORMATER_10 ="ddMMMyy";
    public static final String FORMATER_11 ="ddMMMyyyy";
    public static final String FORMATER_12 ="MM/yyyy";
    public static final String FORMATER_13 ="HH:mm";
    public static final String FORMATER_14 ="yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
    public static final String YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
    public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
    public static final String YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd HH:mm:ss:SSS";

    public static final String FORMATER_YYYY_MM_DD = "yyyy-MM-dd";
    public static final String FORMATER_YYYY_MM_DD_HH_mm_ss = "yyyy-MM-dd HH:mm:ss";
    public static final String FORMATER_HH_mm = "HH:mm";
    public static final String FORMATER_MM_DD = "MM-dd";
    public static final String FORMATER_DD = "dd";


    /**
     * 转换日期格式,? str1 转为 str2
     *
     * @param
     * @return
     */
    public static synchronized String convertDateFormat(SimpleDateFormat format1, SimpleDateFormat format2, String time) {
        try {
            Date date = format1.parse(time);
            return format2.format(date);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 转换日期格式,? str1 转为 str2
     *
     * @param
     * @return
     */
    public static synchronized String convertStrToDate(SimpleDateFormat format1, SimpleDateFormat format2, String time) {
        try {
            Date date = format1.parse(time);
            return format2.format(date);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static synchronized String convertDateToStr(SimpleDateFormat format, Date time) {
        try {
            return format.format(time);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static synchronized String convertFormatToStr(SimpleDateFormat format, String originalString) {
        try {
            Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(originalString);
            String newString = format.format(date); //09:00
            return newString;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String addDate(String nowDateString, int days) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String retvDateStr = "";
        try {
            Date sourceDate = sdf.parse(nowDateString);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(sourceDate);
            calendar.add(Calendar.DATE, days);
            Date targetDate = calendar.getTime();
            retvDateStr = sdf.format(targetDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return retvDateStr;
    }

    public static String addTime(Date insertTime, long i) {

        long time = insertTime.getTime() + i * 1000;
        String date = getSimpleDateFormat(FORMATER_13).format(new Date(time));
        return date;
    }

    public static String addMinutes(Date insertTime, long i) {
        long time = insertTime.getTime() + i * 60 * 1000;
        String date = getSimpleDateFormat(FORMATER_3).format(new Date(time));
        return date;
    }


    public static final String getTime() {
        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
    }
    public static final String getTimeSSS() {
        return dateTimeNow(YYYY_MM_DD_HH_MM_SS_SSS);
    }

    //根据生日日期计算年龄
    public static Integer getAge(Date birthDay) {
        try {
            //获取当前系统时间
            Calendar cal = Calendar.getInstance();
            //如果出生日期大于当前时间，则抛出异常
            if (cal.before(birthDay)) {
                throw new IllegalArgumentException(
                        "The birthDay is before Now.It's unbelievable!");
            }
            //取出系统当前时间的年、月、日部分
            int yearNow = cal.get(Calendar.YEAR);
            int monthNow = cal.get(Calendar.MONTH);
            int dayOfMonthNow = cal.get(Calendar.DAY_OF_MONTH);

            //将日期设置为出生日期
            cal.setTime(birthDay);
            //取出出生日期的年、月、日部分
            int yearBirth = cal.get(Calendar.YEAR);
            int monthBirth = cal.get(Calendar.MONTH);
            int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
            //当前年份与出生年份相减，初步计算年龄
            int age = yearNow - yearBirth;
            //当前月份与出生日期的月份相比，如果月份小于出生月份，则年龄上?1，表示不满多少周?
            if (monthNow <= monthBirth) {
                //如果月份相等，在比较日期，如果当前日，小于出生日，也?1，表示不满多少周?
                if (monthNow == monthBirth) {
                    if (dayOfMonthNow < dayOfMonthBirth) age--;
                } else {
                    age--;
                }
            }
            return age;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //根据生日日期计算年龄
    public static Integer getAge(String birthday) {
        try {
            Date birthDay = getSimpleDateFormat(FORMATER_2).parse(birthday);
            //获取当前系统时间
            Calendar cal = Calendar.getInstance();
            //如果出生日期大于当前时间，则抛出异常
            if (cal.before(birthDay)) {
                throw new IllegalArgumentException(
                        "The birthDay is before Now.It's unbelievable!");
            }
            //取出系统当前时间的年、月、日部分
            int yearNow = cal.get(Calendar.YEAR);
            int monthNow = cal.get(Calendar.MONTH);
            int dayOfMonthNow = cal.get(Calendar.DAY_OF_MONTH);

            //将日期设置为出生日期
            cal.setTime(birthDay);
            //取出出生日期的年、月、日部分
            int yearBirth = cal.get(Calendar.YEAR);
            int monthBirth = cal.get(Calendar.MONTH);
            int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
            //当前年份与出生年份相减，初步计算年龄
            int age = yearNow - yearBirth;
            //当前月份与出生日期的月份相比，如果月份小于出生月份，则年龄上?1，表示不满多少周?
            if (monthNow <= monthBirth) {
                //如果月份相等，在比较日期，如果当前日，小于出生日，也?1，表示不满多少周?
                if (monthNow == monthBirth) {
                    if (dayOfMonthNow < dayOfMonthBirth) age--;
                } else {
                    age--;
                }
            }
            return age;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String calculateAge(LocalDateTime birthDateTime, LocalDateTime currentDateTime) {

        if (currentDateTime.isBefore(birthDateTime)) {
            return "还没有出生！";
        }

        LocalDate birthDate = birthDateTime.toLocalDate();
        LocalDate currentDate = currentDateTime.toLocalDate();

        Period period = Period.between(birthDate, currentDate);
        long years = period.getYears();
        long months = period.getMonths();
        long days = period.getDays();
        String strRetVal;

        if (years == 0 && months == 0 && days == 0) {
            long hours = (long)(currentDateTime.getHour() - birthDateTime.getHour());
            long minutes = (long)(currentDateTime.getMinute() - birthDateTime.getMinute());
            if (minutes < 0) {
                hours--;
                minutes += 60;
            }
            return hours + "时" + minutes + "分";
        }
        String yearStr = years > 0 ? years + "岁" : "";
        String monthStr = months > 0 ? months + "月" : "";
        String dayStr = days > 0 ? days + "天" : "";

        if (years > 6) {
            strRetVal = yearStr;
        } else if (years >= 2) {
            strRetVal = yearStr + monthStr;
        } else {
            strRetVal = yearStr + monthStr + dayStr;
        }

        return strRetVal;
    }

    /**
     * 根据日期获得星期
     *
     * @param date
     * @return
     */
    public static String getWeekOfDate(Date date) {
        String[] weekDaysName = {"周日", "周一", "周二", "周三", "周四", "周五", "周六"};
//		  String[] weekDaysCode = { "0", "1", "2", "3", "4", "5", "6" };
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int intWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1;
        return weekDaysName[intWeek];
    }

    /**
     * 根据日期获取月份
     *
     * @param date 日期格式Date类型
     * @return 月份
     */
    public static int getMonthOfDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int month = calendar.get(Calendar.MONTH) + 1;
        return month;
    }

    /**
     * 根据日期获取年份
     *
     * @param date 日期格式Date类型
     * @return 年份
     */
    public static int getYearOfDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int year = calendar.get(Calendar.YEAR);
        return year;
    }

    /**
     * 根据日期获取日（几月几日?
     *
     * @param date 日期格式Date类型
     * @return ?
     */
    public static int getDayOfDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        return day;
    }

    /**
     * 在给定日期基?上添加天?
     *
     * @param date 要操作的日期
     * @param num  添加的天?
     * @return
     */
    public static Date addDay(Date date, int num) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, num);
        return calendar.getTime();
    }

    /**
     * 把字符串格式化为日期
     *
     * @param str 要转换为日期的字符串
     * @return 有可能返回NULL 注意NPE的处?
     * @throws Exception
     */
    public static Date formatStringToDate(String str, SimpleDateFormat format) {
        try {
            return format.parse(str);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 判断字符串是否为?
     *
     * @param str 要操作的字符?
     * @return
     */
    private static boolean isEmpty(String str) {
        if (str == null || "".equals(str) || "null".equals(str)) {
            return true;
        }
        return false;
    }

    /**
     * 获取pattern
     *
     * @param pattern 日期的格?
     * @return
     */
    public static SimpleDateFormat getSimpleDateFormat(String pattern) {
        return new SimpleDateFormat(pattern);
    }

    /**
     * 把日期格式化为字符串
     *
     * @param date    要格式化的日?
     * @param pattern 日期的格?
     * @return
     */
    public static String formatDateToString(Date date, String pattern) {
        if (pattern == null || "".equals(pattern)) {
            pattern = YYYY_MM_DD_HH_MM;
        }
        if (date == null) {
            return null;
        }
        SimpleDateFormat sdf = getSimpleDateFormat(pattern);
        return sdf.format(date);
    }




    public static int getWeek() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        int weekIdx = calendar.get(Calendar.DAY_OF_WEEK) - 1;
//			switch (weekIdx) {
//				case 1:
//					System.out.println("今天是星期一");
//					break;
//				case 2:
//					System.out.println("今天是星期二");
//					break;
//				case 3:
//					System.out.println("今天是星期三");
//					break;
//				case 4:
//					System.out.println("今天是星期四");
//					break;
//				case 5:
//					System.out.println("今天是星期五");
//				case 6:
//					System.out.println("今天是星期六");
//					break;
//				default:
//					System.out.println("今天是星期日");
//					break;
//			}
        return weekIdx;

    }

    public static Date localdatetimeToDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    public static LocalDateTime dateToLocalDateTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * @Description: LocalDateTime转字符串格式
     * @Author:马舜雄
     * @Date: 2023/3/2 16:52
     * @Params: [date, format]
     * @ParamTypes: [java.util.Date, java.lang.String]
     * @Return: java.lang.String
     **/
    public static String localDateTime2Str(LocalDateTime localDateTime, String format) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format, Locale.CHINA);
        return formatter.format(localDateTime);
    }


    /**
     * @Description: 获取日期所在月份的第一天。时分秒都是0
     * @Author:马舜雄
     * @Date: 2023/3/8 14:21
     * @Params: [date]
     * @ParamTypes: [java.util.Date]
     * @Return: java.util.Date
     **/
    public static Date getMonthFirstDay(Date date) {
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);//获得本月第一天calendar.add(Calendar.MONTH, 0);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * @Description: 获取日期对应的下月第一天.时分秒都是0
     * @Author:马舜雄
     * @Date: 2023/3/8 14:21
     * @Params: [date]
     * @ParamTypes: [java.util.Date]
     * @Return: java.util.Date
     **/
    public static Date getNextMonthFirstDay(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, 1);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * @Description: 获取日期所在月份的最后一天
     * @Author:马舜雄
     * @Date: 2023/3/8 14:21
     * @Params: [date]
     * @ParamTypes: [java.util.Date]
     * @Return: java.util.Date
     **/
    public static Date getMonthLastDay(Date date) {
        Date nextMonthFirstDay = getNextMonthFirstDay(date);
        return addDay(nextMonthFirstDay, -1);
    }

    /**
     * @Description: 判断date是否在时间范围内(not ( date < dateFrom | | date > dateTo))
     * @Author:马舜雄
     * @Date: 2023/3/8 14:36
     * @Params: [date, dateFrom, dateTo]
     * @ParamTypes: [java.util.Date, java.util.Date, java.util.Date]
     * @Return: java.lang.Boolean
     **/
    public static Boolean dateInRange(Date date, Date dateFrom, Date dateTo) {
        return !(date.before(dateFrom) || date.after(dateTo));
    }

    public static final String parseDateToStr(final String format, final Date date) {
        return new SimpleDateFormat(format).format(date);
    }

    public static final String dateTimeNow(final String format) {
        return parseDateToStr(format, new Date());
    }

    public static final String getCurrDate() {
        return dateTimeNow("yyyy-MM-dd HH:mm:ss");
    }

    /**
     * @Description: 把时间去掉时分秒
     * @Author:马舜雄
     * @Date: 2023/3/8 14:56
     * @Params: [date]
     * @ParamTypes: [java.util.Date]
     * @Return: java.util.Date
     **/
    public static Date truncDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }


    //计算两个时间相差多少小时
    public static int hourBetween(String smdate, String bdate) {
        int retv = 0;
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            Date d1 = df.parse(smdate);
            Date d2 = df.parse(bdate);
            long diff = d1.getTime() - d2.getTime();
            long hours = diff / 3600000L;
            retv = Integer.parseInt(String.valueOf(hours));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return retv;
    }
    //天数差
    /**
     * 计算两个时间的时间差
     * @param begin     开始时间
     * @param end       结束时间
     * @param dateUnit  单位
     * @return      根据单位最终得到星期、天、时、分、秒
     */
    public static int between(@NonNull Date begin, @NonNull Date end, DateEnum dateUnit) {
        long diff = end.getTime() - begin.getTime();
        return (int) (diff / dateUnit.getMillis());
    }


    public static String getCurrentTimeAddHour(String smdate, int hour) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Calendar cal = Calendar.getInstance();
            cal.setTime(sdf.parse(smdate));
            cal.add(Calendar.HOUR, hour);
            return sdf.format(cal.getTime());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static synchronized String getTime24() {
        try {
            return getSimpleDateFormat(FORMATER_3).format(new Date());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据一个给定的开始日期和输入日期，计算从给定的开始日期起的第N个7天周期，并显示该周期的所有日期
     * @Author:江蛟
     * @param startDate 基准日期作为7天排部的第一天
     * @param inputDate 输入某个日期
     * @return
     */
    public static List<LocalDate> getPeriodDates(LocalDate startDate, LocalDate inputDate,Integer dayNum) {
        if(dayNum == null){
            dayNum = 7;
        }
        // 计算输入日期距离开始日期有多少天
        long daysBetween = ChronoUnit.DAYS.between(startDate, inputDate);

        // 计算当前日期所在的周期的开始日期
        LocalDate periodStartDate = startDate.plusDays((daysBetween / dayNum) * dayNum);

        // 创建一个列表来存储该周期的所有日期
        List<LocalDate> periodDates = new ArrayList<>();

        // 添加7天的日期到列表中
        for (int i = 0; i < dayNum; i++) {
            periodDates.add(periodStartDate.plusDays(i));
        }

        return periodDates;
    }

    /**
     * LocalDate to Date, init time at start of day
     *
     * @param date 日期
     * @return {@link Date}
     */
    public static Date covert2Date(LocalDate date) {
        return Date.from(date.atStartOfDay()
                .atZone(ZoneId.systemDefault())
                .toInstant());
    }
}
